﻿

namespace Tentacle.Downloader
{
    using System;
    using System.Linq;
    using System.Collections.Generic;
    using System.Net;
    using System.Text;
    using System.Net.Sockets;

    using static Utils.TentacleProtocol;
    using static Utils.Hepler;

    sealed class ClientService : IClientService
    {

        Socket  _socket;
        bool    _sendPendding;
        byte[]  _buffer;

        #region Constructor

        /// <summary>
        /// 
        /// </summary>
        public ClientService() : this(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) { }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="addressFamily"></param>
        /// <param name="socketType"></param>
        /// <param name="protocolType"></param>
        public ClientService(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
        {
            var ips = from e in Dns.GetHostAddresses(Dns.GetHostName()) where e.AddressFamily == addressFamily select e;
            if (ips.Count() == 0) {
                throw new ArgumentException("无法绑定本地IP地址");
            }

            var ip = ips.First();

            _socket = new Socket(addressFamily, socketType, protocolType);
            _socket.Bind(new IPEndPoint(ip, 0));
            _buffer = new byte[4096];
        }

        #endregion

        #region Public Method

        public void Dispose()
        {
            _socket.Dispose();
        }

        public void Connect(IPEndPoint iP)
        {
            _socket.Connect(iP);
        }

        public void SetToken(string token)
        {
            if (_socket.Connected && !_sendPendding) {
                _sendPendding = true;
                try {
                    // Send

                    var array = Encoding.UTF8.GetBytes($"<{Set_Token},{token}>");
                    _socket.Send(array);

                    // Receive
                    var count = _socket.Receive(_buffer);
                    var msg = Encoding.UTF8.GetString(_buffer,0,count);
                    switch (msg) {
                        case Response_ERROR:
                            break;
                        case Response_SUCCESS:
                            break;
                    }

                }
                catch {
                    _sendPendding = false;
                }
            }
        }

        public void SetSite(string site)
        {
            if (_socket.Connected && !_sendPendding) {
                _sendPendding = true;
                try {
                    // Send

                    var array = Encoding.UTF8.GetBytes($"<{Set_Site},{site}>");
                    _socket.Send(array);

                    // Receive Header
                    var count = _socket.Receive(_buffer);
                    var length = BitConverter.ToInt32(_buffer,0);

                    if(length > 0) {
                        array = new byte[length];

                        // Response Success
                        _socket.Send(Encoding.UTF8.GetBytes(Response_SUCCESS));

                        // Receive Data

                        count = _socket.Receive(array);
                        var msg = Encoding.UTF8.GetString(array, 0, count);
                        if(msg.Substring(0,5) != Response_ERROR) {
                            DownloadedCompleted?.Invoke(msg);
                        }
                        else {
                            DownloadedErrorRaised?.Invoke(msg.Split(',')[1]);
                        }
                    }
                    else {
                        // Receive Error 
                        _socket.Send(Encoding.UTF8.GetBytes(Response_ERROR));
                    }

                    _sendPendding = false;

                }
                catch(SocketException se) {
                    Trace(se.Message);
                    Disconnected?.Invoke();
                    DownloadedErrorRaised?.Invoke(se.Message);
                }
                catch(Exception ex) {                    
                    _sendPendding = false;                    
                    DownloadedErrorRaised?.Invoke(ex.Message);
                }
            }
        }

        public void Disconnet()
        {
            if (_socket.Connected && !_sendPendding) {
                _sendPendding = true;
                try {
                    // Send

                    var array = Encoding.UTF8.GetBytes(Disconnect);
                    _socket.Send(array);

                    _socket.Disconnect(true);
                }
                catch {
                    _sendPendding = false;
                }
            }
        }

        #endregion

        #region Property

        public string Guid {
            get {
                return System.Guid.NewGuid().ToString();
            }
        }

        public bool Connected {
            get {
                return _socket?.Connected ?? false;
            }
        }

        public IPEndPoint LocalEndPoint {
            get {
                return (IPEndPoint)_socket?.RemoteEndPoint;
            }
        }

        #endregion

        public event Action<string> DownloadedCompleted;
        public event Action<string> DownloadedErrorRaised;
        public event Action Disconnected;
    }
}
